home *** CD-ROM | disk | FTP | other *** search
/ Developer CD Series 1995…tember: Reference Library / Dev.CD Sep 95 RL / Dev.CD Sep 95 RL.toast / mac / Technical Documentation / develop / develop Issue 23 code / Multipane Dialogs Code / ListControl.c < prev    next >
Encoding:
C/C++ Source or Header  |  1995-03-11  |  45.7 KB  |  1,806 lines  |  [TEXT/MMCC]

  1. #include "Utilities.h"
  2.  
  3. /*
  4. ** Apple Macintosh Developer Technical Support
  5. **
  6. ** Program:         listcontrol.c
  7. ** Written by:      Eric Soldan
  8. **
  9. ** Copyright © 1991 Apple Computer, Inc.
  10. ** All rights reserved.
  11. */
  12.  
  13. /* You may incorporate this sample code into your applications without
  14. ** restriction, though the sample code has been provided "AS IS" and the
  15. ** responsibility for its operation is 100% yours.  However, what you are
  16. ** not permitted to do is to redistribute the source as "DSC Sample Code"
  17. ** after having made changes. If you're going to re-distribute the source,
  18. ** we require that you make it clear in the source that the code was
  19. ** descended from Apple Sample Code, but that you've made changes. */
  20.  
  21. /*
  22. **
  23. ** To create a List control, you only need a single call.  For example:
  24. **
  25. **    list = CLNew(rViewCtl,                                    Resource ID of view control for List control.
  26. **                 true,                                         Control initially visible.
  27. **                 &viewRct,                                    View rect of list.
  28. **                 numRows,                                    Number of rows to create List with.
  29. **                 numCols,                                    Number of columns to create List with.
  30. **                 cellHeight,
  31. **                 cellWidth,
  32. **                 theLProc,                                    Custom List procedure resource ID.
  33. **                 window,                                    Window to hold List control.
  34. **                 clHScroll | clShowActive | clActive        Horizontal scrollbar, active List.
  35. **    );
  36. **
  37.  
  38. ** If the CLNew call succeeds, you then have a List control in your
  39. ** window.  It will be automatically disposed of when you close the window.
  40. ** If you don't want this to happen, then you can detach it from the
  41. ** view control which owns it.  To do this, you would to the following:
  42. **
  43. **  viewCtl = CLViewFromList(theListHndl);
  44. **  if (viewCtl) SetControlReference(viewCtl, nil);
  45. **
  46. ** The view control keeps a reference to the List record in the refCon.
  47. ** If the refCon is cleared, then the view control does nothing.  So, all that
  48. ** is needed to detach a List record from a view control is to set the
  49. ** view control's refCon nil.  Now if you close the window, you will still
  50. ** have the List record.
  51. **
  52. **
  53. ** To remove a List control completely from a window, just dispose of the view
  54. ** control that holds the List record.  To do this, just do something like the below:
  55. **
  56. **  DisposeControl(CLViewFromList(theListHndl));
  57. **
  58. ** This completely disposes of the List control.
  59. **
  60. **
  61. ** Events for the List record are handled nearly automatically.  Just make the
  62. ** following call:
  63. **
  64. **  CLClick(window, eventPtr, &action);
  65. **
  66. ** If the event was handled, true is returned.  If the event is false, then the
  67. ** event doesn't belong to a List control, and further processing of the event
  68. ** should be done.
  69. */
  70.  
  71.  
  72.  
  73. /*****************************************************************************/
  74.  
  75.  
  76.  
  77. #ifndef __CONTROLS__
  78. #include <Controls.h>
  79. #endif
  80.  
  81. #ifndef __ERRORS__
  82. #include <Errors.h>
  83. #endif
  84.  
  85. #ifndef __LISTCONTROL__
  86. #include "ListControl.h"
  87. #endif
  88.  
  89. #ifndef __LOWMEM__
  90. #include <LowMem.h>
  91. #endif
  92.  
  93. #ifndef __MEMORY__
  94. #include <Memory.h>
  95. #endif
  96.  
  97. #ifndef __PACKAGES__
  98. #include <Packages.h>
  99. #endif
  100.  
  101. #ifndef __RESOURCES__
  102. #include <Resources.h>
  103. #endif
  104.  
  105. #ifndef __TEXTUTILS__
  106. #include <TextUtils.h>
  107. #endif
  108.  
  109.  
  110.  
  111. /*****************************************************************************/
  112.  
  113.  
  114.  
  115. #define kListPosTextLen 32
  116. #define kPrevSel        16
  117.  
  118. #ifdef powerc
  119. #pragma options align=mac68k
  120. #endif
  121. typedef struct cdefRsrcJMP {
  122.     long    jsrInst;
  123.     long    moveInst;
  124.     short    jmpInst;
  125.     long    jmpAddress;
  126. } cdefRsrcJMP;
  127. typedef cdefRsrcJMP *cdefRsrcJMPPtr, **cdefRsrcJMPHndl;
  128. #ifdef powerc
  129. #pragma options align=reset
  130. #endif
  131.  
  132.  
  133.  
  134. /*****************************************************************************/
  135.  
  136.  
  137.  
  138. short    gListCtl = rListCtl;
  139.  
  140. static long                        gLastKeyTime;
  141. static char                        gListPosText[kListPosTextLen];
  142. static short                    gListPosTextLen;
  143. static cdefRsrcJMPHndl            gCDEF;
  144. static CLGetCompareDataProcPtr    gGetCompareDataProc;
  145. static CLDoCompareDataProcPtr    gDoCompareDataProc;
  146. static ListHandle                gCLVList;
  147. static ControlHandle            gVFLView;
  148. static ListHandle                gVFLList;
  149. static Rect                        gLRect;
  150.  
  151. static void            CLVScrollContent(short h, short v);
  152. static WindowPtr    CLVSetClip(ListHandle list);
  153.  
  154. static void            CLInitialize(void);
  155. static void            CLBorderDraw(ListHandle listHndl);
  156. static pascal long    CLCtl(short varCode, ControlHandle ctl, short msg, long parm);
  157. static pascal short    MyIntlCompare(Ptr aPtr, Ptr bPtr, short aLen, short bLen);
  158.  
  159. static void                dummyCLActivate(Boolean makeActive, ListHandle listHndl);
  160. static Boolean            dummyCLClick(WindowPtr window, EventRecord *event, short *action);
  161. static ControlHandle    dummyCLCtlHit(void);
  162. static ListHandle        dummyCLFindActive(WindowPtr window);
  163. static Boolean            dummyCLKey(WindowPtr window, EventRecord *event);
  164. static ControlHandle    dummyCLNext(WindowPtr window, ListHandle *listHndl, ControlHandle ctl, short dir, Boolean justActive);
  165. static ControlHandle    dummyCLViewFromList(ListHandle listHndl);
  166. static ListHandle        dummyCLWindActivate(WindowPtr window, Boolean displayIt);
  167.  
  168. CLActivateProcPtr        gclActivate       = dummyCLActivate;
  169. CLClickProcPtr            gclClick          = dummyCLClick;
  170. CLCtlHitProcPtr            gclCtlHit         = dummyCLCtlHit;
  171. CLFindActiveProcPtr        gclFindActive     = dummyCLFindActive;
  172. CLKeyProcPtr            gclKey            = dummyCLKey;
  173. CLNextProcPtr            gclNext           = dummyCLNext;
  174. CLViewFromListProcPtr    gclViewFromList   = dummyCLViewFromList;
  175. CLWindActivateProcPtr    gclWindActivate   = dummyCLWindActivate;
  176.  
  177.  
  178. CLVVariableSizeCellsProcPtr    gclvVariableSizeCells = nil;
  179. CLVGetCellRectProcPtr        gclvGetCellRect       = nil;
  180. CLVUpdateProcPtr            gclvUpdate            = nil;
  181. CLVAutoScrollProcPtr        gclvAutoScroll        = nil;
  182. CLVSetSelectProcPtr            gclvSetSelect         = nil;
  183. CLVClickProcPtr                gclvClick             = nil;
  184. CLVAdjustScrollBarsProcPtr  gclvAdjustScrollBars  = nil;
  185.  
  186.  
  187. extern short    gPrintPage;        /* Non-zero means we are printing. */
  188.  
  189.  
  190.  
  191. /*****************************************************************************/
  192.  
  193.  
  194.  
  195. static ListHandle        gFoundLHndl;
  196.     /* Global value used to return info from the List control proc. */
  197.  
  198. static ControlHandle    gFoundViewCtl;
  199.     /* Global value used to return info from the List control proc. */
  200.  
  201.  
  202.  
  203. /*****************************************************************************/
  204. /*****************************************************************************/
  205.  
  206. #ifdef applec
  207. #pragma segment ATGListControl
  208. #endif
  209.  
  210. /*****************************************************************************/
  211. /*****************************************************************************/
  212.  
  213.  
  214.  
  215. /* Instead of calling the functions directly, you can reference the global
  216. ** proc pointers that reference the functions.  This keeps everything from
  217. ** being linked in.  The default global proc pointers point to dummy functions
  218. ** that behave as if there aren't any list controls.  The calls can still be
  219. ** made, yet the runtime behavior is such that it will operate as if there
  220. ** no instances of the List control.  This allows intermediate code to access
  221. ** the functions or not without automatically linking in all sorts of stuff
  222. ** into the application that isn't desired.  To change the global proc pointers
  223. ** so that they point to the actual functions, just call CLInitialize() once
  224. ** in the beginning of the application.  If CLInitialize() is referenced, it will
  225. ** get linked in.  In turn, everything that it references directly or indirectly
  226. ** will get linked in. */
  227.  
  228. static void    CLInitialize(void)
  229. {
  230.     if (gclActivate != CLActivate) {
  231.         gclActivate       = CLActivate;
  232.         gclClick          = CLClick;
  233.         gclCtlHit         = CLCtlHit;
  234.         gclFindActive     = CLFindActive;
  235.         gclKey            = CLKey;
  236.         gclNext           = CLNext;
  237.         gclViewFromList   = CLViewFromList;
  238.         gclWindActivate   = CLWindActivate;
  239.     }
  240. }
  241.  
  242.  
  243.  
  244. /*****************************************************************************/
  245.  
  246.  
  247.  
  248. /* Activate this List record.  Activation is NOT done by calling LActivate().
  249. ** The active control is indicated by the 2-pixel thick border around the
  250. ** List control.  This allows all List controls in a window to display which
  251. ** cells are selected.  This behavior can be overridden by calling LActivate()
  252. ** on the List record for List controls.
  253. ** Human interface dictates that only at most a single List control has this
  254. ** active border.  For this reason, this function scans for other List
  255. ** controls in the window and removes the border from any other that it finds. */
  256.  
  257. void    CLActivate(Boolean makeActive, ListHandle listHndl)
  258. {
  259.     WindowPtr        window, oldPort;
  260.     ControlHandle    viewCtl;
  261.     short            oldDisplay, newDisplay;
  262.     ListHandle        list;
  263.     CLDataHndl        listData;
  264.  
  265.     if (listHndl) {
  266.         window = (WindowPtr)(*listHndl)->port;
  267.         for (viewCtl = nil;;) {
  268.             viewCtl = CLNext(window, &list, viewCtl, 1, false);
  269.             if (!viewCtl) break;
  270.             listData   = (CLDataHndl)(*viewCtl)->contrlData;
  271.             oldDisplay = (*listData)->mode;
  272.             newDisplay = (oldDisplay & (0xFFFF - clActive));
  273.             if (makeActive)
  274.                 if (list == listHndl)
  275.                     newDisplay |= clActive;
  276.             if (oldDisplay != newDisplay) {
  277.                 (*listData)->mode = newDisplay;
  278.                 GetPort(&oldPort);
  279.                 SetPort(window);
  280.                 CLBorderDraw(list);
  281.                 SetPort(oldPort);
  282.             }
  283.         }
  284.     }
  285. }
  286.  
  287.  
  288.  
  289. static void    dummyCLActivate(Boolean makeActive, ListHandle listHndl)
  290. {
  291. #ifndef __MWERKS__
  292. #pragma unused (makeActive, listHndl)
  293. #endif
  294. }
  295.  
  296.  
  297.  
  298. /*****************************************************************************/
  299.  
  300.  
  301.  
  302. static void    CLBorderDraw(ListHandle listHndl)
  303. {
  304.     ControlHandle    viewCtl;
  305.     WindowPtr        oldPort, listPort;
  306.     short            displayInfo;
  307.     PenState        oldPen;
  308.     CLDataHndl        listData;
  309.  
  310.     SetRect(&gLRect, 0, 0, 0, 0);
  311.     if (listHndl) {
  312.         viewCtl = CLViewFromList(listHndl);
  313.         if (viewCtl) {
  314.             if ((*viewCtl)->contrlVis) {
  315.                 GetPort(&oldPort);
  316.                 SetPort(listPort = (*listHndl)->port);
  317.                 GetPenState(&oldPen);
  318.                 PenNormal();
  319.                 listData    = (CLDataHndl)(*viewCtl)->contrlData;
  320.                 displayInfo = (*listData)->mode;
  321.                 gLRect = (*listHndl)->rView;
  322.                 InsetRect(&gLRect, -1, -1);
  323.                 FrameRect(&gLRect);
  324.                 if (displayInfo & clShowActive) {
  325.                     gLRect = (*listHndl)->rView;
  326.                     InsetRect(&gLRect, -4, -4);
  327.                     if ((*listHndl)->vScroll)
  328.                         gLRect.right  += 15;
  329.                     if ((*listHndl)->hScroll)
  330.                         gLRect.bottom += 15;
  331.                     PenSize(2, 2);
  332.                     if ((!((WindowPeek)listPort)->hilited) || (!(displayInfo & clActive)))
  333.                         PenPat((ConstPatternParam)&qd.white);
  334.                     FrameRect(&gLRect);
  335.                 }
  336.                 SetPenState(&oldPen);
  337.                 SetPort(oldPort);
  338.             }
  339.         }
  340.     }
  341. }
  342.  
  343.  
  344.  
  345. /*****************************************************************************/
  346.  
  347.  
  348.  
  349. /* This is called when a mouseDown occurs in the content of a window.  It
  350. ** returns true if the mouseDown caused a List action to occur.  Events
  351. ** that are handled include if the user clicks on a scrollbar that is
  352. ** associated with a List control. */
  353.  
  354. Boolean    CLClick(WindowPtr window, EventRecord *event, short *action)
  355. {
  356.     WindowPtr        oldPort;
  357.     Point            mouseLoc;
  358.     ListHandle        list;
  359.     ControlHandle    ctlHit, viewCtl;
  360.     CLDataHndl        listData;
  361.     short            mode;
  362.     unsigned char    ohlt;
  363.  
  364.     if (action)
  365.         *action = 0;
  366.     gLastKeyTime = 0;
  367.  
  368.     GetPort(&oldPort);
  369.     if (!((WindowPeek)window)->hilited) return(false);
  370.  
  371.     SetPort(window);
  372.     mouseLoc = event->where;
  373.     GlobalToLocal(&mouseLoc);
  374.  
  375.     if (!(viewCtl = CLFindCtl(window, event, &list, &ctlHit))) return(false);
  376.  
  377.     if (!list) {
  378.         SetPort(oldPort);
  379.         return(false);
  380.     }        /* Didn't hit list control or related scrollbar.  No action taken. */
  381.  
  382.     if (CLFindActive(window) != list) {            /* If not active list control, activate it.       */
  383.         if ((*viewCtl)->contrlHilite != 255) {
  384.             CLActivate(true, list);                /* Now is the active list control.               */
  385.             if (action)                            /* CLClick can be called again if the control  */
  386.                 *action = -1;                    /* activates and operates with the same click. */
  387.             SetPort(oldPort);
  388.             return(true);
  389.         }
  390.     }
  391.  
  392.     UseControlStyle(viewCtl);
  393.     listData = (CLDataHndl)(*viewCtl)->contrlData;
  394.     mode     = (*listData)->mode;
  395.     if (mode & clVariable) {
  396.         if ((*gclvClick)(mouseLoc, event->modifiers, list))
  397.             if (action)
  398.                 *action = 1;        /* If double-click, then return that it was. */
  399.     }
  400.     else {
  401.         ohlt = (*viewCtl)->contrlHilite;
  402.         (*viewCtl)->contrlHilite = -1;
  403.             /* System 6 has a bug.  It calls FindControl to see if a List scrollbar was hit.  If it finds a control,
  404.             ** it assumes that it is going to be a list control of its own.  If it can't match against either the vert
  405.             ** or horiz scrollbar, it exits.  This is of course really bad for a list control, since the body of the
  406.             ** list is a control, and therefore FindControl will return something that doesn't match the scrollbars.
  407.             ** This only fixes the problem if the list control isn't on top of any other controls.  Controls beneath
  408.             ** the list will need to be temporarily inactivated by the application. */
  409.  
  410.         if (LClick(mouseLoc, event->modifiers, list))
  411.             if (action)
  412.                 *action = 1;        /* If double-click, then return that it was. */
  413.         (*viewCtl)->contrlHilite = ohlt;
  414.     }
  415.     UseControlStyle(nil);
  416.  
  417.     SetPort(oldPort);
  418.     return(true);
  419. }
  420.  
  421.  
  422.  
  423. static Boolean    dummyCLClick(WindowPtr window, EventRecord *event, short *action)
  424. {
  425. #ifndef __MWERKS__
  426. #pragma unused (window, event)
  427. #endif
  428.  
  429.     if (action)
  430.         *action = 0;
  431.     return(false);
  432. }
  433.  
  434.  
  435.  
  436. /*****************************************************************************/
  437.  
  438.  
  439.  
  440. static pascal long    CLCtl(short varCode, ControlHandle ctl, short msg, long parm)
  441. {
  442. #ifndef __MWERKS__
  443. #pragma unused (varCode)
  444. #endif
  445.  
  446.     Rect            viewRct;
  447.     ListHandle        list;
  448.     WindowPtr        curPort, ww;
  449.     ControlHandle    vScroll, hScroll;
  450.     CLDataHndl        listData;
  451.  
  452.     list = (ListHandle)GetControlReference(ctl);
  453.     if (list)
  454.         viewRct = (*list)->rView;
  455.     else
  456.         SetRect(&viewRct, 0, 0, 0, 0);
  457.  
  458.     switch (msg) {
  459.         case drawCntl:
  460.             GetPort(&curPort);
  461.             vScroll = (*list)->vScroll;
  462.             if (vScroll) {
  463.                 ww = (*vScroll)->contrlOwner;
  464.                 if (!((WindowPeek)ww)->hilited) (*list)->vScroll = nil;
  465.             }
  466.             hScroll = (*list)->hScroll;
  467.             if (hScroll) {
  468.                 ww = (*hScroll)->contrlOwner;
  469.                 if (!((WindowPeek)ww)->hilited) (*list)->hScroll = nil;
  470.             }
  471.             CLUpdate(curPort->visRgn, list);
  472.             (*list)->vScroll = vScroll;
  473.             (*list)->hScroll = hScroll;
  474.             CLBorderDraw(list);
  475.             break;
  476.  
  477.         case testCntl:
  478.             if (PtInRect(*(Point *)&parm, &viewRct)) {
  479.                 gFoundViewCtl = ctl;
  480.                 gFoundLHndl   = list;
  481.                 return(1);
  482.             }
  483.             return(0);
  484.             break;
  485.  
  486.         case calcCRgns:
  487.         case calcCntlRgn:
  488.             if (msg == calcCRgns)
  489.                 parm &= 0x00FFFFFF;
  490.             RectRgn((RgnHandle)parm, &viewRct);
  491.             break;
  492.  
  493.         case initCntl:
  494.             break;
  495.  
  496.         case dispCntl:
  497.             if (list) {
  498.                 GetPort(&curPort);
  499.                 SetPort((*list)->port);
  500.                 gVFLView = ctl;
  501.                 gVFLList = list;
  502.                 listData = (CLDataHndl)(*ctl)->contrlData;
  503.                 (*listData)->mode &= (0xFFFF - clActive);
  504.                 CLBorderDraw(list);
  505.                 EraseRect(&gLRect);
  506.                 InvalRect(&gLRect);
  507.                 gVFLView = nil;
  508.                 gVFLList = nil;
  509.                 LDispose(list);
  510.                 DisposeHandle((Handle)(*ctl)->contrlData);
  511.                 SetPort(curPort);
  512.             }
  513.             break;
  514.  
  515.         case posCntl:
  516.             break;
  517.  
  518.         case thumbCntl:
  519.             break;
  520.  
  521.         case dragCntl:
  522.             break;
  523.  
  524.         case autoTrack:
  525.             break;
  526.     }
  527.  
  528.     return(0);
  529. }
  530.  
  531.  
  532.  
  533. /*****************************************************************************/
  534.  
  535.  
  536.  
  537. /* The List control that was hit by calling FindControl is saved in a
  538. ** global variable, since the CDEF has no way of returning what kind it was.
  539. ** To determine that it was a List control that was hit, first call this
  540. ** function.  The first call returns the old value in the global variable,
  541. ** plus it resets the global to nil.  Then call FindControl(), and then
  542. ** call this function again.  If it returns nil, then a List control
  543. ** wasn't hit.  If it returns non-nil, then it was a List control that
  544. ** was hit, and specifically the one returned. */
  545.  
  546. ControlHandle    CLCtlHit(void)
  547. {
  548.     ControlHandle    ctl;
  549.  
  550.     ctl = gFoundViewCtl;
  551.     gFoundViewCtl = nil;
  552.     return(ctl);
  553. }
  554.  
  555.  
  556.  
  557. static ControlHandle    dummyCLCtlHit(void)
  558. {
  559.     return(nil);
  560. }
  561.  
  562.  
  563.  
  564. /*****************************************************************************/
  565.  
  566.  
  567.  
  568. /* Handle the event if it applies to the active List control.  If some
  569. ** action occured due to the event, return true. */
  570.  
  571. Boolean    CLEvent(WindowPtr window, EventRecord *event, short *action)
  572. {
  573.     WindowPtr    clickWindow;
  574.     short        actn;
  575.  
  576.     if (action)
  577.         *action = 0;
  578.  
  579.     switch(event->what) {
  580.  
  581.         case mouseDown:
  582.             if (FindWindow(event->where, &clickWindow) == inContent)
  583.                 if (window == clickWindow)
  584.                     if (((WindowPeek)window)->hilited) return(CLClick(window, event, action));
  585.             break;
  586.  
  587.         case autoKey:
  588.         case keyDown:
  589.             if (!(event->modifiers & cmdKey)) {
  590.                 actn = CLKey(window, event);
  591.                 if (action)
  592.                     *action = actn;
  593.                 if (actn) return(true);
  594.             }
  595.             break;
  596.     }
  597.  
  598.     return(false);
  599. }
  600.  
  601.  
  602.  
  603. /*****************************************************************************/
  604.  
  605.  
  606.  
  607. /* Returns the active List control, if any.  Unlike the TextEdit control, passing
  608. ** in nil doesn't return the currently active control independent of window.  The
  609. ** only reason that the TextEdit control returns the "globally active" control is
  610. ** so that the TEIdle procedure can do its thing.  The List control doesn't have
  611. ** such a thing, so there is no purpose for a "globally active" control.  If the
  612. ** window pointer passed in is nil (requesting the "globally active" List control),
  613. ** we just return nil, indicating that there isn't one. */
  614.  
  615. ListHandle    CLFindActive(WindowPtr window)
  616. {
  617.     ControlHandle    viewCtl;
  618.     ListHandle        list;
  619.     short            display;
  620.     CLDataHndl        listData;
  621.  
  622.     if (!window) return(nil);
  623.  
  624.     for (viewCtl = nil;;) {
  625.         viewCtl = CLNext(window, &list, viewCtl, 1, true);
  626.         if (!viewCtl) break;
  627.         listData = (CLDataHndl)(*viewCtl)->contrlData;
  628.         display  = (*listData)->mode;
  629.         if (display & clActive) break;
  630.     }
  631.     return(list);
  632. }
  633.  
  634.  
  635.  
  636. static ListHandle    dummyCLFindActive(WindowPtr window)
  637. {
  638. #ifndef __MWERKS__
  639. #pragma unused (window)
  640. #endif
  641.     return(nil);
  642. }
  643.  
  644.  
  645.  
  646. /*****************************************************************************/
  647.  
  648.  
  649.  
  650. /* This determines if a List control was clicked on directly.  This does
  651. ** not determine if a related scrollbar was clicked on.  If a List
  652. ** control was clicked on, then true is returned, as well as the List
  653. ** handle and the handle to the view control. */
  654.  
  655. ControlHandle    CLFindCtl(WindowPtr window, EventRecord *event, ListHandle *listHndl, ControlHandle *ctlHit)
  656. {
  657.     WindowPtr        oldPort;
  658.     Point            mouseLoc;
  659.     ControlHandle    ctl, listctl;
  660.     ListHandle        list;
  661.  
  662.     if (window) {
  663.         GetPort(&oldPort);
  664.         SetPort(window);
  665.         mouseLoc = event->where;
  666.         GlobalToLocal(&mouseLoc);
  667.         SetPort(oldPort);
  668.  
  669.         gFoundLHndl = nil;
  670.  
  671.         if (!WhichControl(mouseLoc, 0, window, &ctl)) return(nil);
  672.             /* Didn't hit a thing, so forget it. */
  673.  
  674.         list = CLFromScroll(ctl, &listctl);
  675.         if (list) {
  676.             if (ctlHit)
  677.                 *ctlHit = ctl;
  678.             if (listHndl)
  679.                 *listHndl = list;
  680.             return(listctl);
  681.         }
  682.  
  683.         FindControl(mouseLoc, window, &ctl);
  684.         if (!ctl)                                                        return(nil);
  685.         if (StripAddress((*ctl)->contrlDefProc) != StripAddress(gCDEF)) return(nil);
  686.             /* Control hit was above List control, so we didn't hit a List control. */
  687.         if (ctlHit)
  688.             *ctlHit = ctl;
  689.         if (listHndl)
  690.             *listHndl = gFoundLHndl;
  691.         if (gFoundLHndl) return(ctl);
  692.     }
  693.  
  694.     if (listHndl)
  695.         *listHndl = nil;
  696.     if (ctlHit)
  697.         *ctlHit = nil;
  698.     return(nil);
  699. }
  700.  
  701.  
  702.  
  703. /*****************************************************************************/
  704.  
  705.  
  706.  
  707. /* Find the List record that is related to the indicated scrollbar. */
  708.  
  709. ListHandle    CLFromScroll(ControlHandle scrollCtl, ControlHandle *retCtl)
  710. {
  711.     WindowPtr        window;
  712.     ControlHandle    viewCtl;
  713.     ListHandle        list;
  714.  
  715.     *retCtl = nil;
  716.     if (!IsScrollBar(scrollCtl)) return(nil);
  717.  
  718.     window = (*scrollCtl)->contrlOwner;
  719.  
  720.     for (*retCtl = viewCtl = nil;;) {
  721.         viewCtl = CLNext(window, &list, viewCtl, 1, false);
  722.         if (!viewCtl) return(nil);
  723.         list = (ListHandle)GetControlReference(viewCtl);
  724.         if (
  725.             ((*list)->vScroll == scrollCtl) || 
  726.             ((*list)->hScroll == scrollCtl)
  727.         ) {
  728.             *retCtl = viewCtl;
  729.             return(list);
  730.         }
  731.     }
  732. }
  733.  
  734.  
  735.  
  736. /*****************************************************************************/
  737.  
  738.  
  739.  
  740. /* Get the Nth List control in the control list of a window. */
  741.  
  742. ListHandle    CLGetList(WindowPtr window, short lnum)
  743. {
  744.     ControlHandle    ctl;
  745.     ListHandle        list;
  746.  
  747.     for (ctl = nil; lnum--; ctl = CLNext(window, &list, ctl, 1, false)) {};
  748.     return(list);
  749. }
  750.  
  751.  
  752.  
  753. /*****************************************************************************/
  754.  
  755.  
  756.  
  757. /* Insert a cell alphabetically into the list.  Whichever parameter is passed in
  758. ** as -1, either row or column, that is the dimension that is determined. */
  759.  
  760. short    CLInsert(ListHandle listHndl, char *data, short dataLen, short row, short col)
  761. {
  762.     short            loc, len;
  763.     Point            cell;
  764.     char            cstr[256];
  765.     CLDataHndl        listData;
  766.     ControlHandle    viewCtl;
  767.  
  768.     if (!listHndl) return(-1);
  769.  
  770.     gGetCompareDataProc = nil;
  771.     viewCtl = CLViewFromList(listHndl);
  772.     if (viewCtl) {
  773.         listData = (CLDataHndl)(*viewCtl)->contrlData;
  774.         if (listData)
  775.             gGetCompareDataProc = (*listData)->getCompareData;
  776.     }
  777.  
  778.     if (gGetCompareDataProc) {
  779.         (*gGetCompareDataProc)(data, dataLen, cstr, &len);
  780.         loc = CLRowOrColSearch(listHndl, cstr, len, row, col);
  781.     }
  782.     else loc = CLRowOrColSearch(listHndl, data, dataLen, row, col);
  783.  
  784.     if (row == -1) {
  785.         LAddRow(1, cell.v = loc, listHndl);
  786.         cell.h = col;
  787.     }
  788.     else {
  789.         LAddColumn(1, cell.h = loc, listHndl);
  790.         cell.v = row;
  791.     }
  792.  
  793.     LSetCell(data, dataLen, cell, listHndl);
  794.     return(loc);
  795. }
  796.  
  797.  
  798.  
  799. /*****************************************************************************/
  800.  
  801.  
  802.  
  803. /* See if the keypress event applies to the List control, and if it does,
  804. ** handle it and return true. */
  805.  
  806. Boolean    CLKey(WindowPtr window, EventRecord *event)
  807. {
  808.     ListHandle            list;
  809.     ControlHandle        listCtl;
  810.     short                key, mode, thresh;
  811.     long                ll;
  812.     Point                cell, dcell, mm, oldCell, lastCell;
  813.     Rect                bnds, visCells, rct;
  814.     CLDataHndl            listData;
  815.     CLKeyFilterProcPtr    kproc;
  816.  
  817.     list = CLFindActive(window);
  818.     if (list) {
  819.  
  820.         listCtl  = CLViewFromList(list);
  821.         listData = (CLDataHndl)(*listCtl)->contrlData;
  822.         mode     = (*listData)->mode;
  823.         if (!(mode & clKeyPos)) return(false);
  824.  
  825.         kproc = (*listData)->keyFilter;
  826.         if (kproc)
  827.             if ((*kproc)(list, event))
  828.                 return(true);
  829.  
  830.         bnds = (*list)->dataBounds;
  831.         if (bnds.top == bnds.bottom) return(true);
  832.         if (bnds.left == bnds.right) return(true);
  833.             /* The list is empty, so whatever was typed has been "handled". */
  834.  
  835.         if (mode & clVariable) {
  836.             ++bnds.top;
  837.             ++bnds.left;
  838.         }
  839.  
  840.         cell.h = bnds.left;
  841.         cell.v = bnds.top;
  842.         key = event->message & charCodeMask;
  843.  
  844.         if ((key >= chLeft) && (key <= chDown)) {
  845.             if (LGetSelect(true, &cell, list)) key -= kPrevSel;
  846.             else                               cell.h = cell.v = -1;
  847.             for (oldCell = lastCell = cell;; lastCell = cell) {
  848.                 switch (key) {
  849.                     case chLeft - kPrevSel:
  850.                         if (cell.h > bnds.left)
  851.                             --cell.h;
  852.                         break;
  853.                     case chRight - kPrevSel:
  854.                         if (cell.h < bnds.right - 1)
  855.                             ++cell.h;
  856.                         break;
  857.                     case chUp - kPrevSel:
  858.                         if (cell.v > bnds.top)
  859.                             --cell.v;
  860.                         break;
  861.                     case chDown - kPrevSel:
  862.                         if (cell.v < bnds.bottom - 1)
  863.                             ++cell.v;
  864.                         break;
  865.                     case chLeft:
  866.                         cell.v = (*list)->visible.top;
  867.                         cell.h = bnds.right - 1;
  868.                         break;
  869.                     case chRight:
  870.                         cell.v = (*list)->visible.top;
  871.                         cell.h = bnds.left;
  872.                         break;
  873.                     case chUp:
  874.                         cell.h = (*list)->visible.left;
  875.                         cell.v = bnds.bottom - 1;
  876.                         break;
  877.                     case chDown:
  878.                         cell.h = (*list)->visible.left;
  879.                         cell.v = bnds.top;
  880.                         break;
  881.                 }
  882.                 if (!(mode & clVariable)) break;
  883.                 if ((cell.h == lastCell.h) && (cell.v == lastCell.v)) break;
  884.                 (*gclvGetCellRect)(list, cell.h, cell.v, &rct);
  885.                 if (!EmptyRect(&rct)) break;
  886.                 if (key > (chDown - kPrevSel)) key -= kPrevSel;
  887.             }
  888.             if (mode & clVariable) {
  889.                 (*gclvGetCellRect)(list, cell.h, cell.v, &rct);
  890.                 if (EmptyRect(&rct)) cell = oldCell;
  891.                 if (cell.h == -1) return(true);
  892.             }
  893.         }
  894.         else {
  895.             thresh = LMGetKeyThresh();;
  896.             if (thresh > 60) thresh = 60;
  897.             thresh <<= 1;
  898.             if (gLastKeyTime + thresh < event->when)        /* Too long, reset char collection. */
  899.                 gListPosTextLen = 0;
  900.             gLastKeyTime = event->when;
  901.             if (gListPosTextLen < kListPosTextLen)
  902.                 gListPosText[gListPosTextLen++] = key;
  903.             gGetCompareDataProc = (*listData)->getCompareData;
  904.             gDoCompareDataProc  = (*listData)->doCompareData;
  905.             if (!LGetSelect(true, &cell, list)) cell.h = (*list)->visible.left;
  906.             cell.v = CLRowOrColSearch(list, gListPosText, gListPosTextLen, -1, cell.h);
  907.             if (cell.v >= bnds.bottom) cell.v = bnds.bottom - 1;
  908.         }
  909.  
  910.         UseControlStyle(listCtl);
  911.  
  912.         if (mode & clVariable)
  913.             (*gclvSetSelect)(true, cell, list);
  914.         else
  915.             LSetSelect(true, cell, list);        /* Select cell that is closest. */
  916.  
  917.         dcell.h = bnds.left;
  918.         dcell.v = bnds.top;
  919.         for (;;) {                /* Deselect old cells. */
  920.             if (!LGetSelect(true, &dcell, list)) break;
  921.             if ((dcell.h == cell.h) && (dcell.v == cell.v)) {
  922.                 if (++dcell.h >= bnds.right) {
  923.                     dcell.h = bnds.left;
  924.                     ++dcell.v;
  925.                 }
  926.             }
  927.             else {
  928.                 if (mode & clVariable)
  929.                     (*gclvSetSelect)(false, dcell, list);
  930.                 else
  931.                     LSetSelect(false, dcell, list);
  932.             }
  933.         }
  934.         UseControlStyle(nil);
  935.  
  936.         visCells = (*list)->visible;
  937.         if (PtInRect(cell, &visCells)) return(true);        /* Already in view. */
  938.  
  939.         UseControlStyle(listCtl);            /* Scroll into view the way we want it done. */
  940.         if (mode & clVariable) (*gclvAutoScroll)(list);
  941.         else {
  942.             ll = PinRect(&visCells, cell);
  943.             mm = *(Point *)≪
  944.             LScroll(cell.h - mm.h, cell.v - mm.v, list);
  945.         }
  946.         UseControlStyle(nil);
  947.         return(true);
  948.     }
  949.  
  950.     return(false);
  951. }
  952.  
  953.  
  954.  
  955. static Boolean    dummyCLKey(WindowPtr window, EventRecord *event)
  956. {
  957. #ifndef __MWERKS__
  958. #pragma unused (window, event)
  959. #endif
  960.     return(false);
  961. }
  962.  
  963.  
  964.  
  965. /*****************************************************************************/
  966.  
  967.  
  968.  
  969. /* Create a new List control.  See the comments at the beginning of this
  970. ** file for more information.  Note that this function doesn't get a dummy,
  971. ** as you really mean to have this code if you call it.  It calls CLInitialize(),
  972. ** just to make sure that the proc pointers are set to point to the real function,
  973. ** instead of the dummy functions.  By having CLNew() call CLInitialize(), the
  974. ** application won't have to worry about calling CLInitialize().  By the time that
  975. ** the application is using a List control, the proc pointers will be initialized. */
  976.  
  977. ListHandle    CLNew(short viewID, Boolean vis, Rect *vrct, short numRows, short numCols,
  978.                   short cellHeight, short cellWidth, short theLProc, WindowPtr window, short mode)
  979. {
  980.     WindowPtr        oldPort;
  981.     Rect            viewRct, dataBnds, rct;
  982.     Point            cellSize;
  983.     ListHandle        list;
  984.     Boolean            err;
  985.     ControlHandle    viewCtl;
  986.     Boolean            drawIt, hasGrow;
  987.     short            hScroll, vScroll;
  988.     CLDataHndl        listData;
  989.     ControlHandle    ctl;
  990.     RgnHandle        oldClip, newClip;
  991.  
  992.     static ControlDefUPP    cdefupp;
  993.  
  994.     CLInitialize();            /* Make sure that this code gets linked in, in case the application
  995.                             ** is using the proc pointers.  CtlHandler.c uses the proc pointers
  996.                             ** so that if the application doesn't use List controls, this code
  997.                             ** won't get linked in. */
  998.     GetPort(&oldPort);
  999.     SetPort(window);
  1000.  
  1001.     viewRct         = *vrct;
  1002.     dataBnds.top    = dataBnds.left = 0;
  1003.     dataBnds.right  = numCols;
  1004.     dataBnds.bottom = numRows;
  1005.  
  1006.     cellSize.h = cellWidth;
  1007.     cellSize.v = cellHeight;
  1008.  
  1009.     drawIt  = (mode & clDrawIt)  ? 1 : 0;
  1010.     hScroll = (mode & clHScroll) ? 1 : 0;
  1011.     vScroll = (mode & clVScroll) ? 1 : 0;
  1012.     hasGrow = (mode & clHasGrow) ? 1 : 0;
  1013.  
  1014.     viewCtl = nil;
  1015.  
  1016.     if (!vis) {
  1017.         GetClip(oldClip = NewRgn());
  1018.         SetClip(newClip = NewRgn());
  1019.     }
  1020.     list = LNew(&viewRct, &dataBnds, cellSize, theLProc, window, false, hasGrow, hScroll, vScroll);
  1021.     if (!vis) {
  1022.         if (list) {
  1023.             if ((*list)->hScroll) HideControl((*list)->hScroll);
  1024.             if ((*list)->vScroll) HideControl((*list)->vScroll);
  1025.         }
  1026.         SetClip(oldClip);
  1027.         DisposeRgn(oldClip);
  1028.         DisposeRgn(newClip);
  1029.     }
  1030.  
  1031.     err = false;
  1032.     if (list) {        /* If we were able to create the List record... */
  1033.  
  1034.         if ((hasGrow) && (hScroll + vScroll == 1)) {
  1035.             ctl = (*list)->hScroll;
  1036.             if (ctl) {
  1037.                 rct = (*ctl)->contrlRect;
  1038.                 if (rct.right >= viewRct.right)
  1039.                     SizeControl(ctl, viewRct.right - viewRct.left - 13, rct.bottom - rct.top);
  1040.             }
  1041.             ctl = (*list)->vScroll;
  1042.             if (ctl) {
  1043.                 rct = (*ctl)->contrlRect;
  1044.                 if (rct.bottom >= viewRct.bottom)
  1045.                     SizeControl(ctl, rct.right - rct.left, viewRct.bottom - viewRct.top - 13);
  1046.             }
  1047.         }
  1048.  
  1049.         if (!gCDEF) {
  1050.             gCDEF = (cdefRsrcJMPHndl)GetResource('CDEF', (viewID / 16));
  1051.             if (gCDEF) {
  1052.                 if (!cdefupp) {
  1053.                     cdefupp = NewControlDefProc(CLCtl);
  1054.                 }
  1055.                 (*gCDEF)->jmpAddress = (long)cdefupp;
  1056. #if GENERATING68K
  1057.                if (TrapExists(_HWPriv))
  1058.                    FlushInstructionCache();
  1059.                        /* Make sure that instruction caches don't kill us. */
  1060. #endif
  1061.             }
  1062.             else err = true;
  1063.         }
  1064.  
  1065.         if (!err)
  1066.             viewCtl = NewControl(window, &viewRct, "\p", false, 0, 0, 0, viewID, (long)list);
  1067.                 /* Use our custom view cdef.  It's wierd, but it's small. */
  1068.                 /* We have to create the control initially invisible because we haven't */
  1069.                 /* initialized all of the data needed by the update procedure.  If it   */
  1070.                 /* is created visible, the update procedure will be immediately called, */
  1071.                 /* and this would be very bad to do until all of the data is there.     */
  1072.  
  1073.         mode &= (0xFFFF - clDrawIt);
  1074.  
  1075.         if (!viewCtl) err = true;
  1076.  
  1077.         if (!err) {
  1078.             (*viewCtl)->contrlData = nil;
  1079.             listData = (CLDataHndl)NewHandleClear(sizeof(CLDataRec));
  1080.             if (listData) {
  1081.                 (*listData)->mode      = mode & (0xFFFF - clVariable);
  1082.                 (*viewCtl)->contrlData = (Handle)listData;
  1083.                 if (mode & clVariable)
  1084.                     if (gclvVariableSizeCells)
  1085.                         (*gclvVariableSizeCells)(list);
  1086.                 if (vis) {
  1087.                     LSetDrawingMode(drawIt, list);
  1088.                     if (vis) ShowStyledControl(viewCtl);
  1089.                         /* Now that the data is initialized, we can show the control. */
  1090.                 }
  1091.                 else
  1092.                     if (drawIt)
  1093.                         (*list)->listFlags &= (0xFFFF - 0x08);
  1094.                             /* The silly ListMgr shows the scrollbars if we just call LDoDraw. */
  1095.                 if (mode & clActive)
  1096.                     CLActivate(true, list);
  1097.             }
  1098.             else err = true;
  1099.         }
  1100.     }
  1101.     else err = true;
  1102.  
  1103.     SetPort(oldPort);
  1104.  
  1105.     if (err) {        /* Oops.  Somebody wasn't happy. */
  1106.         if (viewCtl)
  1107.             DisposeControl(viewCtl);
  1108.                 /* This also disposes of the List handle! */
  1109.         else
  1110.             if (list)
  1111.                 LDispose(list);
  1112.                     /* We have to dispose of the List handle ourselves if
  1113.                     ** creating the view control failed. */
  1114.  
  1115.         list = nil;        /* Return that there is no List control. */
  1116.     }
  1117.  
  1118.     return(list);
  1119. }
  1120.  
  1121.  
  1122.  
  1123. /*****************************************************************************/
  1124.  
  1125.  
  1126.  
  1127. /* Get the next List control in the window.  You pass it a control handle
  1128. ** for the view control, or nil to start at the beginning of the window.
  1129. ** It returns both a List handle and the view control handle for that
  1130. ** List record.  If none is found, nil is returned.  This allows you to
  1131. ** repeatedly call this function and walk through all the List controls
  1132. ** in a window. */
  1133.  
  1134. ControlHandle    CLNext(WindowPtr window, ListHandle *listHndl, ControlHandle ctl, short dir, Boolean justActive)
  1135. {
  1136.     ControlHandle    nextCtl, priorCtl;
  1137.  
  1138.     if (listHndl)
  1139.         *listHndl = nil;
  1140.  
  1141.     if (!window) return(nil);
  1142.     if (!gCDEF)  return(nil);
  1143.  
  1144.     if (dir > 0) {
  1145.         if (!ctl)
  1146.             ctl = ((WindowPeek)window)->controlList;
  1147.         else
  1148.             ctl = (*ctl)->nextControl;
  1149.         while (ctl) {
  1150.             if ((!justActive) || ((*ctl)->contrlVis)) {
  1151.                 if ((!justActive) || ((*ctl)->contrlHilite != 255)) {
  1152.                     if (StripAddress((*ctl)->contrlDefProc) == StripAddress(gCDEF)) {
  1153.                             /* The handle may be locked, which means that the hi-bit
  1154.                             ** may be on, thus invalidating the compare.  Dereference the
  1155.                             ** handles to get rid of this possibility. */
  1156.                         if (listHndl)
  1157.                             *listHndl = (ListHandle)GetControlReference(ctl);
  1158.                         return(ctl);
  1159.                     }
  1160.                 }
  1161.             }
  1162.             ctl = (*ctl)->nextControl;
  1163.         }
  1164.         return(ctl);
  1165.     }
  1166.  
  1167.     nextCtl = ((WindowPeek)window)->controlList;
  1168.     for (priorCtl = nil; ;nextCtl = (*nextCtl)->nextControl) {
  1169.         if ((!nextCtl) || (nextCtl == ctl)) {
  1170.             if (priorCtl)
  1171.                 if (listHndl)
  1172.                     *listHndl = (ListHandle)GetControlReference(priorCtl);
  1173.             return(priorCtl);
  1174.         }
  1175.         if ((!justActive) || ((*nextCtl)->contrlVis)) {
  1176.             if ((!justActive) || ((*nextCtl)->contrlHilite != 255)) {
  1177.                 if (StripAddress((*nextCtl)->contrlDefProc) == StripAddress(gCDEF))
  1178.                     priorCtl = nextCtl;
  1179.                         /* The handle may be locked, which means that the hi-bit
  1180.                         ** may be on, thus invalidating the compare.  Dereference the
  1181.                         ** handles to get rid of this possibility. */
  1182.             }
  1183.         }
  1184.     }
  1185. }
  1186.  
  1187.  
  1188.  
  1189. static ControlHandle    dummyCLNext(WindowPtr window, ListHandle *listHndl, ControlHandle ctl, short dir, Boolean justActive)
  1190. {
  1191. #ifndef __MWERKS__
  1192. #pragma unused (window, ctl, dir, justActive)
  1193. #endif
  1194.     *listHndl = nil;
  1195.     return(nil);
  1196. }
  1197.  
  1198.  
  1199.  
  1200. /*****************************************************************************/
  1201.  
  1202.  
  1203.  
  1204. /* From the starting for or column, print as many cells as will fit into the
  1205. ** designated rect.  Pass in a starting row and column, and they will be
  1206. ** adjusted to indicate the first cell that didn't fit into the rect.  If all
  1207. ** remaining cells were printed, the row is returned as -1.  The bottom of the
  1208. ** rect to print in is also adjusted to indicate where the actual cut-off
  1209. ** point was. */
  1210.  
  1211. void    CLPrint(RgnHandle clipRgn, ListHandle listHndl, short *row, short *col,
  1212.                 short leftEdge, Rect *drawRct)
  1213. {
  1214.     Rect        dataBnds, keepView, keepVis;
  1215.     Point        csize;
  1216.     short        h, v;
  1217.     RgnHandle    rgn;
  1218.  
  1219.     if (!listHndl) return;
  1220.  
  1221.     dataBnds = (*listHndl)->dataBounds;
  1222.     if ((*col < dataBnds.left) || (*col >= dataBnds.right))
  1223.         *col = leftEdge;
  1224.     if (*row < dataBnds.top)
  1225.         *row = dataBnds.top;
  1226.     if (*row >= dataBnds.bottom) {
  1227.         *row = -1;
  1228.         return;
  1229.     }
  1230.  
  1231.     keepView = (*listHndl)->rView;
  1232.     csize    = (*listHndl)->cellSize;
  1233.     keepVis  = (*listHndl)->visible;
  1234.  
  1235.     h = (drawRct->right - drawRct->left) / csize.h;
  1236.     if (!h)
  1237.         ++h;
  1238.     v = (drawRct->bottom - drawRct->top) / csize.v;
  1239.     if (!v)
  1240.         ++v;
  1241.  
  1242.     if (*col + h > dataBnds.right)
  1243.         h = dataBnds.right  - *col;
  1244.     if (*row + v > dataBnds.bottom)
  1245.         v = dataBnds.bottom - *row;
  1246.  
  1247.     drawRct->bottom = drawRct->top + v * csize.v;
  1248.  
  1249.     (*listHndl)->rView = *drawRct;
  1250.     (*listHndl)->visible.right  = ((*listHndl)->visible.left = *col) + h;
  1251.     (*listHndl)->visible.bottom = ((*listHndl)->visible.top  = *row) + v;
  1252.  
  1253.     if (!(rgn = clipRgn)) {
  1254.         rgn = NewRgn();
  1255.         RectRgn(rgn, drawRct);
  1256.     }
  1257.     CLUpdate(rgn, listHndl);
  1258.     if (!clipRgn)
  1259.         DisposeRgn(rgn);
  1260.  
  1261.     (*listHndl)->rView   = keepView;
  1262.     (*listHndl)->visible = keepVis;
  1263.  
  1264.     *col += h;
  1265.     if (*col >= dataBnds.right) {
  1266.         *col = leftEdge;
  1267.         *row += v;
  1268.         if (*row >= dataBnds.bottom)
  1269.             *row = -1;
  1270.     }
  1271. }
  1272.  
  1273.  
  1274.  
  1275. /*****************************************************************************/
  1276.  
  1277.  
  1278.  
  1279. /* Find the location in the list where the data would belong if inserted.  The row
  1280. ** and column are passed in.  If either is -1, that is the dimension that will be
  1281. ** determined and returned. */
  1282.  
  1283. short    CLRowOrColSearch(ListHandle listHndl, void *data, short dataLen, short row, short col)
  1284. {
  1285.     ControlHandle    viewCtl;
  1286.     Rect            dataBnds;
  1287.     short            numCells, baseCell, pow, loc, cdataLen, varSize;
  1288.     Point            cell;
  1289.     Str255            cdata, gcdata;
  1290.     CLDataHndl        listData;
  1291.  
  1292.     if (!listHndl) return(-1);
  1293.  
  1294.     gGetCompareDataProc = nil;
  1295.     gDoCompareDataProc  = nil;
  1296.  
  1297.     varSize = 0;
  1298.     viewCtl = CLViewFromList(listHndl);
  1299.     if (viewCtl) {
  1300.         listData = (CLDataHndl)(*viewCtl)->contrlData;
  1301.         if (listData) {
  1302.             gGetCompareDataProc = (*listData)->getCompareData;
  1303.             gDoCompareDataProc  = (*listData)->doCompareData;
  1304.             if ((*listData)->mode & clVariable) ++varSize;
  1305.         }
  1306.     }
  1307.  
  1308.     dataBnds = (*listHndl)->dataBounds;
  1309.     if (row == -1)
  1310.         numCells = dataBnds.bottom - (baseCell = dataBnds.top);
  1311.     else
  1312.         numCells = dataBnds.right  - (baseCell = dataBnds.left);
  1313.             /* Get some reference info on the size/start of the list. */
  1314.  
  1315.     if (varSize) {
  1316.         ++baseCell;
  1317.         --numCells;
  1318.     }
  1319.  
  1320.     cell.v = cell.h = varSize;
  1321.  
  1322.     for (;numCells > baseCell; --numCells) {        /* Exclude empty end cells. */
  1323.         if (row == -1)
  1324.             cell.v = baseCell + numCells - 1;
  1325.         else
  1326.             cell.h = baseCell + numCells - 1;
  1327.         cdataLen = 1;
  1328.         LGetCell(cdata, &cdataLen, cell, listHndl);
  1329.         if (cdataLen) break;
  1330.     }
  1331.  
  1332.     if (numCells) {
  1333.         if (row != -1)
  1334.             cell.v = row;
  1335.         if (col != -1)
  1336.             cell.h = col;
  1337.         for (pow = 1; pow < numCells; pow <<= 1) {};
  1338.         pow >>= 1;        /* pow = 2^n such that pow < numCells. */
  1339.  
  1340.         for (loc = pow; pow;) {            /* Do binary search for where to insert. */
  1341.             if (loc >= numCells)
  1342.                 loc = numCells - 1;        /* Off the end is bad. */
  1343.             if (row == -1)
  1344.                 cell.v = baseCell + loc;
  1345.             else
  1346.                 cell.h = baseCell + loc;
  1347.             pow >>= 1;
  1348.  
  1349.             cdataLen = 255;
  1350.             LGetCell(cdata, &cdataLen, cell, listHndl);        /* Get cell data to compare against. */
  1351.             if (gGetCompareDataProc) {
  1352.                 (*gGetCompareDataProc)(cdata, cdataLen, gcdata, &cdataLen);
  1353.                 BlockMove(gcdata, cdata, cdataLen);
  1354.             }
  1355.  
  1356.             if (gDoCompareDataProc)        /* Adjust location based on compare result. */
  1357.                 loc += (pow * (*gDoCompareDataProc)(data, cdata, dataLen, cdataLen));
  1358.             else
  1359.                 loc += (pow * IUMagString(data, cdata, dataLen, cdataLen));
  1360.         }
  1361.  
  1362.         /* The binary search got us close, but not exact.  We may be off by one
  1363.         ** in either direction.  (The binary search can't position in front of
  1364.         ** the first cell in the list, for example.)  Do a linear compare from
  1365.         ** this point to find the correct cell we should insert in front of. */
  1366.  
  1367.  
  1368.         if (loc < numCells) {                    /* Move to the first duplicate. */
  1369.             for (; --loc >= 0;) {
  1370.                 if (row == -1) cell.v = baseCell + loc;
  1371.                 else           cell.h = baseCell + loc;
  1372.                 cdataLen = 255;
  1373.                 LGetCell(cdata, &cdataLen, cell, listHndl);
  1374.                 if (gDoCompareDataProc) {
  1375.                     if ((*gDoCompareDataProc)(data, cdata, dataLen, cdataLen)) break;
  1376.                 }
  1377.                 else {
  1378.                     if (IUMagString(data, cdata, dataLen, cdataLen)) break;
  1379.                 }
  1380.             }
  1381.             ++loc;
  1382.         }
  1383.  
  1384.         if (loc) --loc;                /* Start linear search one back. */
  1385.         for (;; ++loc) {
  1386.             if (row == -1)
  1387.                 cell.v = baseCell + loc;
  1388.             else
  1389.                 cell.h = baseCell + loc;
  1390.             if (loc >= numCells) break;
  1391.             cdataLen = 255;
  1392.             LGetCell(cdata, &cdataLen, cell, listHndl);
  1393.             if (gGetCompareDataProc) {
  1394.                 (*gGetCompareDataProc)(cdata, cdataLen, gcdata, &cdataLen);
  1395.                 BlockMove(gcdata, cdata, cdataLen);
  1396.             }
  1397.  
  1398.             if (gDoCompareDataProc) {
  1399.                 if ((*gDoCompareDataProc)(data, cdata, dataLen, cdataLen) < 1) break;
  1400.             }
  1401.             else {
  1402.                 if (IUMagString(data, cdata, dataLen, cdataLen) < 1) break;
  1403.             }        /* If we are less than or equal to this cell, we have
  1404.                     ** found our insertion point, so break. */
  1405.         }
  1406.     }
  1407.  
  1408.     if (row == -1) return(cell.v);
  1409.     else           return(cell.h);
  1410. }
  1411.  
  1412.  
  1413.  
  1414. /*****************************************************************************/
  1415.  
  1416.  
  1417.  
  1418. /* Draw the List control in the correct form. */
  1419.  
  1420. void    CLUpdate(RgnHandle clipRgn, ListHandle listHndl)
  1421. {
  1422.     WindowPtr        curPort, listPort;
  1423.     ControlHandle    ctl;
  1424.     Rect            view, vis, bnds, r;
  1425.     RgnHandle        rgn1, rgn2;
  1426.     CLDataHndl        listData;
  1427.     short            mode;
  1428.  
  1429.     if (listHndl) {
  1430.  
  1431.         ctl = CLViewFromList(listHndl);
  1432.         if (ctl) {
  1433.  
  1434.             GetPort(&curPort);
  1435.  
  1436.             listPort = (*listHndl)->port;
  1437.             (*listHndl)->port = curPort;
  1438.  
  1439.             listData = (CLDataHndl)(*ctl)->contrlData;
  1440.             mode     = (*listData)->mode;
  1441.             if (mode & clVariable) (*gclvUpdate)(clipRgn, listHndl);
  1442.             else                   LUpdate(clipRgn, listHndl);
  1443.  
  1444.             (*listHndl)->port = listPort;
  1445.  
  1446.             view = (*listHndl)->rView;
  1447.             vis  = (*listHndl)->visible;
  1448.             bnds = (*listHndl)->dataBounds;
  1449.             SectRect(&vis, &bnds, &vis);
  1450.  
  1451.             r = view;
  1452.             if (mode & clVariable) {
  1453.                 if ((--vis.right > 0) && (--vis.bottom > 0)) {
  1454.                     (*gclvGetCellRect)(listHndl, vis.right, vis.bottom, &r);
  1455.                     r.top  = view.top;
  1456.                     r.left = view.left;
  1457.                 }
  1458.                 else SetRect(&r, 0, 0, 0, 0);
  1459.             }
  1460.             else {
  1461.                 r.right  = r.left + (vis.right  - vis.left) * (*listHndl)->cellSize.h;
  1462.                 r.bottom = r.top  + (vis.bottom - vis.top ) * (*listHndl)->cellSize.v;
  1463.             }
  1464.             SectRect(&r, &view, &r);
  1465.  
  1466.             RectRgn(rgn1 = NewRgn(), &view);
  1467.             RectRgn(rgn2 = NewRgn(), &r);
  1468.             DiffRgn(rgn1, rgn2, rgn1);
  1469.             EraseRgn(rgn1);
  1470.             DisposeRgn(rgn1);
  1471.             DisposeRgn(rgn2);
  1472.         }
  1473.     }
  1474. }
  1475.  
  1476.  
  1477.  
  1478. /*****************************************************************************/
  1479.  
  1480.  
  1481.  
  1482. /* Return the control handle for the view control that owns the List
  1483. ** record.  Use this to find the view to do customizations such as changing
  1484. ** the update procedure for this List control. */
  1485.  
  1486. ControlHandle    CLViewFromList(ListHandle listHndl)
  1487. {
  1488.     WindowPtr        window;
  1489.     ControlHandle    viewCtl;
  1490.     ListHandle        list;
  1491.  
  1492.     if (!listHndl) return(nil);
  1493.  
  1494.     if (gVFLView)
  1495.         if (gVFLList == listHndl)
  1496.             return(gVFLView);
  1497.  
  1498.     window = (WindowPtr)(*listHndl)->port;
  1499.     for (viewCtl = nil;;) {
  1500.         viewCtl = CLNext(window, &list, viewCtl, 1, false);
  1501.         if ((!viewCtl) || (list == listHndl)) return(viewCtl);
  1502.     }
  1503. }
  1504.  
  1505.  
  1506.  
  1507. static ControlHandle    dummyCLViewFromList(ListHandle listHndl)
  1508. {
  1509. #ifndef __MWERKS__
  1510. #pragma unused (listHndl)
  1511. #endif
  1512.     return(nil);
  1513. }
  1514.  
  1515.  
  1516.  
  1517. /*****************************************************************************/
  1518.  
  1519.  
  1520.  
  1521. /* This window is becoming active or inactive.  The borders of the List
  1522. ** controls need to be redrawn due to this.  For each List control in the
  1523. ** window, redraw the active border. */
  1524.  
  1525. ListHandle    CLWindActivate(WindowPtr window, Boolean displayIt)
  1526. {
  1527.     ListHandle    list;
  1528.  
  1529.     gLastKeyTime = 0;        /* Restart the entry collection for Lists. */
  1530.  
  1531.     if (!window) return(nil);
  1532.  
  1533.     list = CLFindActive(window);
  1534.     if (list) {
  1535.         if (displayIt)
  1536.             CLBorderDraw(list);
  1537.         return(list);
  1538.     }
  1539.  
  1540.     return(nil);
  1541. }
  1542.  
  1543.  
  1544.  
  1545. static ListHandle    dummyCLWindActivate(WindowPtr window, Boolean displayIt)
  1546. {
  1547. #ifndef __MWERKS__
  1548. #pragma unused (window, displayIt)
  1549. #endif
  1550.  
  1551.     return(nil);
  1552. }
  1553.  
  1554.  
  1555.  
  1556. /*****************************************************************************/
  1557.  
  1558.  
  1559.  
  1560. static pascal short    MyIntlCompare(Ptr aPtr, Ptr bPtr, short aLen, short bLen)
  1561. {
  1562.     short    cmp;
  1563.     char    cstr[256];
  1564.  
  1565.     if (gGetCompareDataProc) {
  1566.         (*gGetCompareDataProc)(aPtr, aLen, cstr, &aLen);
  1567.         aPtr = cstr;
  1568.     }
  1569.  
  1570.     if (gDoCompareDataProc)
  1571.         cmp = (*gDoCompareDataProc)(aPtr, bPtr, aLen, bLen);
  1572.     else
  1573.         cmp = IUMagString(aPtr, bPtr, aLen, bLen);
  1574.  
  1575.     if (cmp == -1) return(1);
  1576.     else           return(0);
  1577. }
  1578.  
  1579.  
  1580.  
  1581. /*****************************************************************************/
  1582.  
  1583.  
  1584.  
  1585. void    CLSize(ListHandle list, short newH, short newV)
  1586. {
  1587. #ifndef __MWERKS__
  1588. #pragma unused (oldh, oldv)
  1589. #endif
  1590.  
  1591.     WindowPtr        oldPort;
  1592.     ControlHandle    viewCtl, ctl;
  1593.     Rect            viewRct, rct;
  1594.     RgnHandle        oldClip, newClip;
  1595.     CLDataHndl        listData;
  1596.     short            mode, hScroll, vScroll;
  1597.  
  1598.     if (!(viewCtl = CLViewFromList(list))) return;
  1599.  
  1600.     GetPort(&oldPort);
  1601.     SetPort((*list)->port);
  1602.  
  1603.     viewRct  = (*viewCtl)->contrlRect;
  1604.     listData = (CLDataHndl)(*viewCtl)->contrlData;
  1605.     mode     = (*listData)->mode;
  1606.  
  1607.     GetClip(oldClip = NewRgn());
  1608.     SetClip(newClip = NewRgn());
  1609.         /* Prevent any drawing, since the newly resized scrollbars may not account for
  1610.         ** the growIcon area.  We don't want them temporarily redrawing over the
  1611.         ** growIcon. */
  1612.  
  1613.     LSize(newH, newV, list);
  1614.  
  1615.     hScroll = (mode & clHScroll) ? 1 : 0;
  1616.     vScroll = (mode & clVScroll) ? 1 : 0;
  1617.         /* Get enough info to determine if we need to fix the scrollbar sizes. */
  1618.  
  1619.     viewRct.right  = viewRct.left + newH;
  1620.     viewRct.bottom = viewRct.top  + newV;
  1621.     (*viewCtl)->contrlRect = viewRct;
  1622.  
  1623.     if ((mode & clHasGrow) && (hScroll + vScroll == 1)) {
  1624.         ctl = (*list)->hScroll;
  1625.         if (ctl) {
  1626.             rct = (*ctl)->contrlRect;
  1627.             if (rct.right >= viewRct.right)
  1628.                 SizeControl(ctl, viewRct.right - viewRct.left - 13, rct.bottom - rct.top);
  1629.         }
  1630.         ctl = (*list)->vScroll;
  1631.         if (ctl) {
  1632.             rct = (*ctl)->contrlRect;
  1633.             if (rct.bottom >= viewRct.bottom)
  1634.                 SizeControl(ctl, rct.right - rct.left, viewRct.bottom - viewRct.top - 13);
  1635.         }
  1636.     }
  1637.  
  1638.     SetClip(oldClip);
  1639.     DisposeRgn(oldClip);
  1640.     DisposeRgn(newClip);
  1641.         /* Allow drawing again so we can redisplay the newly resized list control and scrollbars. */
  1642.  
  1643.     InsetRect(&viewRct, -1, -1);        /* Erase all of the old list control and scrollbars. */
  1644.     if (vScroll)
  1645.         viewRct.right += 15;
  1646.     if (hScroll)
  1647.         viewRct.bottom += 15;
  1648.     if (mode & clShowActive)
  1649.         InsetRect(&viewRct, -4, -4);
  1650.     if ((*viewCtl)->contrlVis)
  1651.         EraseRect(&viewRct);
  1652.  
  1653.     if (mode & clVariable) (*gclvAdjustScrollBars)(list);
  1654.  
  1655.     DoDraw1Control(viewCtl, false);
  1656.     SetPort(oldPort);
  1657. }
  1658.  
  1659.  
  1660.  
  1661. /*****************************************************************************/
  1662.  
  1663.  
  1664.  
  1665. void    CLMove(ListHandle list, short newH, short newV)
  1666. {
  1667. #ifndef __MWERKS__
  1668. #pragma unused (oldh, oldv)
  1669. #endif
  1670.  
  1671.     WindowPtr        oldPort;
  1672.     ControlHandle    viewCtl, ctl;
  1673.     Rect            viewRct, rct;
  1674.     RgnHandle        oldClip, newClip;
  1675.     CLDataHndl        listData;
  1676.     short            mode, hScroll, vScroll, dx, dy;
  1677.  
  1678.     if (!(viewCtl = CLViewFromList(list))) return;
  1679.  
  1680.     GetPort(&oldPort);
  1681.     SetPort((*list)->port);
  1682.  
  1683.     viewRct  = (*viewCtl)->contrlRect;
  1684.     listData = (CLDataHndl)(*viewCtl)->contrlData;
  1685.     mode     = (*listData)->mode;
  1686.  
  1687.     dx = newH - viewRct.left;
  1688.     dy = newV - viewRct.top;
  1689.     if ((!dx) && (!dy)) return;
  1690.  
  1691.     hScroll = (mode & clHScroll) ? 1 : 0;
  1692.     vScroll = (mode & clVScroll) ? 1 : 0;
  1693.         /* Get enough info to determine if we need to fix the scrollbar sizes. */
  1694.  
  1695.     rct = viewRct;                /* Erase all of the old list control and scrollbars. */
  1696.     InsetRect(&rct, -1, -1);
  1697.     if (vScroll)
  1698.         rct.right += 15;
  1699.     if (hScroll)
  1700.         rct.bottom += 15;
  1701.     if (mode & clShowActive)
  1702.         InsetRect(&rct, -4, -4);
  1703.     if ((*viewCtl)->contrlVis)
  1704.         EraseRect(&rct);
  1705.  
  1706.     OffsetRect(&viewRct, dx, dy);
  1707.     (*viewCtl)->contrlRect = viewRct;
  1708.     (*list)->rView = viewRct;
  1709.  
  1710.     GetClip(oldClip = NewRgn());
  1711.     SetClip(newClip = NewRgn());
  1712.  
  1713.     ctl = (*list)->hScroll;
  1714.     if (ctl) {
  1715.         rct = (*ctl)->contrlRect;
  1716.         MoveControl(ctl, rct.left + dx, rct.top + dy);
  1717.     }
  1718.  
  1719.     ctl = (*list)->vScroll;
  1720.     if (ctl) {
  1721.         rct = (*ctl)->contrlRect;
  1722.         MoveControl(ctl, rct.left + dx, rct.top + dy);
  1723.     }
  1724.  
  1725.     SetClip(oldClip);
  1726.     DisposeRgn(oldClip);
  1727.     DisposeRgn(newClip);
  1728.  
  1729.     DoDraw1Control(viewCtl, false);
  1730.     SetPort(oldPort);
  1731. }
  1732.  
  1733.  
  1734.  
  1735. /*****************************************************************************/
  1736.  
  1737.  
  1738.  
  1739. /* Show the designated List control and related scrollbars. */
  1740.  
  1741. void    CLShow(ListHandle list)
  1742. {
  1743.     ControlHandle    viewCtl, scrollCtl;
  1744.     short            i;
  1745.  
  1746.     viewCtl = CLViewFromList(list);
  1747.     if (viewCtl) {
  1748.         ShowStyledControl(viewCtl);
  1749.         for (i = 0; i < 2; i++) {
  1750.             scrollCtl = (i) ? (*list)->hScroll : (*list)->vScroll;
  1751.             if (scrollCtl)
  1752.                 ShowStyledControl(scrollCtl);
  1753.         }
  1754.     }
  1755. }
  1756.  
  1757.  
  1758.  
  1759. /*****************************************************************************/
  1760.  
  1761.  
  1762.  
  1763. /* Hide the designated List control and related scrollbars. */
  1764.  
  1765. Rect    CLHide(ListHandle list)
  1766. {
  1767.     ControlHandle    viewCtl, scrollCtl;
  1768.     short            i;
  1769.     WindowPtr        oldPort;
  1770.     CLDataHndl        listData;
  1771.     short            displayInfo;
  1772.     Rect            rct;
  1773.  
  1774.     viewCtl = CLViewFromList(list);
  1775.     if (viewCtl) {
  1776.         HideControl(viewCtl);
  1777.         for (i = 0; i < 2; i++) {
  1778.             scrollCtl = (i) ? (*list)->hScroll : (*list)->vScroll;
  1779.             if (scrollCtl)
  1780.                 HideControl(scrollCtl);
  1781.         }
  1782.     }
  1783.  
  1784.     GetPort(&oldPort);
  1785.     SetPort((*list)->port);
  1786.     listData    = (CLDataHndl)(*viewCtl)->contrlData;
  1787.     displayInfo = (*listData)->mode;
  1788.     rct         = (*list)->rView;
  1789.     InsetRect(&rct, -1, -1);
  1790.     if ((*list)->vScroll)
  1791.         rct.right  += 15;
  1792.     if ((*list)->hScroll)
  1793.         rct.bottom += 15;
  1794.     if (displayInfo & clActive)
  1795.         if (displayInfo & clShowActive)
  1796.             InsetRect(&rct, -3, -3);
  1797.  
  1798.     EraseRect(&rct);
  1799.     SetPort(oldPort);
  1800.  
  1801.     return(rct);
  1802. }
  1803.  
  1804.  
  1805.  
  1806.